// 对所有的属性都要监听，递归遍历所有属性。传过来的对象
function Observer(data) {
  this.data = data;
  // 遍历取出所有的属性
  this.walk(data);
}

Observer.prototype = {
  walk: function(data) {
    var that = this;

    // 遍历取出所有的属性
    Object.keys(data).forEach(key => {
      that.convert(key, data['key']);
    })
  },
  
  convert: function(key, val) {
    this.defineReactive(this.data, key, val);
  },
  
  defineReactive: function(data, key, val) {
    var dep = new Dep(); // 初始化订阅器
    var childObj = observe(val); // 查看属性是否还有子级对象

    // 监听key的值变化。
    Object.defineProperty(data, key, {
      enumerable: true, // 可枚举
      configurable: false, // 不能再define
      get: function() {
        Dep.target && dep.depend(); // 是否增加订阅者
        return val;
      },
      set: function(newVal) {
        if (newVal === val) {
          return;
        }
        val = newVal;
        // 检查新的值，新的值是object的话，递归进行监听
        childObj = observe(newVal);
        // 新的值变化，通知订阅者
        dep.notify();
      }
    });
  }
}

function observe(value, vm) {
  if (!value || typeof value !== 'object') {
    return;
  }

  return new Observer(value);
};

var uid = 0;

// 消息订阅器Dep来专门收集这些订阅者，并在监听器Observer和订阅者Watcher之间进行统一的管理。
function Dep(){
  this.id = uid++;
  this.subs = [];
}

// prototype这个属性只有函数对象才有，具体的说就是构造函数具有.
Dep.prototype = {
  addSub: function(sub) {
    this.subs.push(sub); // 向成员变量（订阅者）里，增加订阅者。
  },

  depend: function() {
    Dep.target.addDep(this);
  },

  removeSub: function(sub) {
    var index = this.subs.indexOf(sub);
    if (index != -1) {
        this.subs.splice(index, 1);
    }
  },

  notify: function() {
    this.subs.forEach(sub => {
      sub.update(); // 通知每个订阅者检查更新 update() 方法用于修改当前集合，可以添加新的元素或集合到当前集合中，如果添加的元素在集合中已存在，则该元素只会出现一次，重复的会忽略。
    })
  }
}

Dep.target = null;